﻿using System;
using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Toybox.CookieGetterSharp.SQLite
{
  partial class SQLiteWrap : IDisposable
  {

    #region Constructor
    public SQLiteWrap(string path)
    {
      _path = path;
    }

    public void Dispose()
    {
      Close();
    }
    #endregion


    #region Public Member

    public bool IsOpened
    {
      get;
      private set;
    }

    #endregion

    #region PrivateMember

    private IntPtr _db;
    private string _path;
    
    #endregion
    

    #region Public Methods

    public void Open()
    {
      Trace.WriteLine("Call Open");
      //IntPtr ip = Marshal.StringToHGlobalAnsi(_path);
      //short hr = sqlite3_open(ip, out _db);
      SQLiteDLL.Result r = SQLiteDLL.sqlite3_open16(_path, out _db);
      if (r != SQLiteDLL.Result.OK)
        throw new SQLiteException(_path + "を開けませんでした:errorcode" + r);

      //Marshal.FreeHGlobal(ip);
      IsOpened = true;
    }

    public void Close()
    {
      Trace.WriteLine("Call Close");
      if (IsOpened)
        SQLiteDLL.sqlite3_close(_db);

      IsOpened = false;
    }

    public void ExecuteNonQuery(string query)
    {
      Trace.WriteLine("Call ExecuteNonQuery");

      if (!IsOpened)
        throw new SQLiteException("SQLiteは開かれていません");

      IntPtr stmHandle = Prepare(query);

      if (SQLiteDLL.sqlite3_step(stmHandle) != SQLiteDLL.Result.DONE)
        throw new SQLiteException("SQL宣言を実行できませんでした");

      Finalize(stmHandle);
    }

    public DataTable ExecuteQuery(string query)
    {
      Trace.WriteLine("Call ExecuteQuery");

      if (!IsOpened)
        throw new SQLiteException("SQLiteは開かれていません");

      IntPtr stmHandle = Prepare(query);
      Trace.WriteLine("StmHandle:" + stmHandle.ToString());

      int columnCount = SQLiteDLL.sqlite3_column_count(stmHandle);
      Trace.WriteLine("Column Count:" + columnCount);

      DataTable dTable = new DataTable();
      for (int i = 0; i < columnCount; i++)
      {
        // こいつが問題

        //string name = sqlite3_column_origin_name(stmHandle, i);

        //string name = sqlite3_column_name(stmHandle, i);
        //Trace.WriteLine("Column[" + i + "]:" + name);
        //dTable.Columns.Add(name);

        //dTable.Columns.Add("column" + i);
#if TRACE
        string name = Marshal.PtrToStringAnsi(SQLiteDLL.sqlite3_column_origin_name(stmHandle, i));
        Trace.WriteLine("Column[" + i + "]:" + name);
        dTable.Columns.Add(name);
#else
        dTable.Columns.Add(Marshal.PtrToStringAnsi(SQLiteDLL.sqlite3_column_origin_name(stmHandle, i)));
#endif
      }

      Trace.WriteLine("Read Rows Start");
      while (SQLiteDLL.sqlite3_step(stmHandle) == SQLiteDLL.Result.ROW)
      {
        Trace.WriteLine("Read Rows");
        object[] row = new object[columnCount];
        for (int i = 0; i < columnCount; i++)
        {
#if TRACE
          SQLiteDLL.DataType type = SQLiteDLL.sqlite3_column_type(stmHandle, i);
          Trace.WriteLine(type.ToString());
          switch (type)
#else
          switch (SQLiteDLL.sqlite3_column_type(stmHandle, i))
#endif
          {
            case SQLiteDLL.DataType.INTERGER:
              row[i] = SQLiteDLL.sqlite3_column_int64(stmHandle, i);
              break;
            case SQLiteDLL.DataType.TEXT:
#if TRACE
              IntPtr ptr = SQLiteDLL.sqlite3_column_text(stmHandle, i);
              Trace.WriteLine(ptr.ToString());
              row[i] = Marshal.PtrToStringAnsi(ptr);
#else
              row[i] = Marshal.PtrToStringAnsi(SQLiteDLL.sqlite3_column_text(stmHandle, i));
#endif
              break;
            case SQLiteDLL.DataType.FLOAT:
              row[i] = SQLiteDLL.sqlite3_column_double(stmHandle, i);
              break;
          }

          Trace.Indent();
          Trace.WriteLine("Value:" + row[i].ToString());
          Trace.Unindent();
        }
        dTable.Rows.Add(row);
      }

      Finalize(stmHandle);

      return dTable;
    }

    private IntPtr Prepare(string query)
    {
      Trace.WriteLine("Call Prepare");

      IntPtr stmHandle;

      if (SQLiteDLL.sqlite3_prepare_v2(_db, query, query.Length, out stmHandle, IntPtr.Zero) != SQLiteDLL.Result.OK)
        throw new SQLiteException(Marshal.PtrToStringAnsi(SQLiteDLL.sqlite3_errmsg(_db)));

      return stmHandle;
    }

    private void Finalize(IntPtr stmHandle)
    {
      Trace.WriteLine("Call Finalize");

      if (SQLiteDLL.sqlite3_finalize(stmHandle) != SQLiteDLL.Result.OK)
        throw new SQLiteException("SQL宣言を終了できませんでした");
    }

    #endregion

  }
}